#include <iostream>
#include <chrono>
#include <immintrin.h>

using namespace std;

const int n[10] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
const int m[10] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
const int p[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};

int lp(int *A, int size) {
    int x = size - 1;
    int count = 0;
    while (!A[count]) {
        x--;
        count++;
    }
    return x;
}

int main() {
    for (int k = 0; k < 10; k++) {
        int* R = new int[n[k] * n[k]];
        int* E = new int[n[k] * n[k]];

        for (int i = 0; i < n[k]; i++) {
            for (int j = 0; j < n[k]; j++) {
                R[i * n[k] + j] = 0;
                E[i * n[k] + j] = 0;
            }
        }

        for (int i = 0; i < m[k]; i++) {
            for (int j = n[k] - p[k]; j < n[k]; j++) {
                E[i * n[k] + j] = 1;
            }
        }

        for (int i = 0; i < p[k]; i++) {
            R[i * n[k] + n[k] - i - 1] = 1;
        }

        auto t1 = chrono::high_resolution_clock::now();

        for (int i = 0; i < m[k]; i++) {
            int IsNull = 0;
            __m256i IsNullHelp = _mm256_setzero_si256();

            for (int j = 0; j + 8 < n[k]; j += 8) {
                __m256i IsNullHelpE = _mm256_loadu_si256((__m256i_u*)&E[i * n[k] + j]);
                IsNullHelp = _mm256_or_si256(IsNullHelp, IsNullHelpE);
            }

            IsNullHelp = _mm256_or_si256(IsNullHelp, _mm256_permute2x128_si256(IsNullHelp, IsNullHelp, 0x81));
            IsNullHelp = _mm256_or_si256(IsNullHelp, _mm256_shuffle_epi32(IsNullHelp, _MM_SHUFFLE(0, 0, 0, 0)));
            int* temp = (int*)&IsNullHelp;
            IsNull |= temp[0] | temp[4];

            while (IsNull) {
                int IsNull2 = 0;
                __m256i IsNull2Help = _mm256_setzero_si256();

                for (int j = 0; j + 8 < n[k]; j += 8) {
                    __m256i IsNull2HelpR = _mm256_loadu_si256((__m256i_u*)&R[lp(&E[i * n[k]], n[k]) * n[k] + j]);
                    IsNull2Help = _mm256_or_si256(IsNull2Help, IsNull2HelpR);
                }

                IsNull2Help = _mm256_or_si256(IsNull2Help, _mm256_permute2x128_si256(IsNull2Help, IsNull2Help, 0x81));
                IsNull2Help = _mm256_or_si256(IsNull2Help, _mm256_shuffle_epi32(IsNull2Help, _MM_SHUFFLE(0, 0, 0, 0)));
                temp = (int*)&IsNull2Help;
                IsNull2 |= temp[0] | temp[4];

                if (IsNull2) {
                    int FirstNum = lp(&E[i * n[k]], n[k]);
                    for (int j = 0; j + 8 < n[k]; j += 8) {
                        __m256i vtE = _mm256_loadu_si256((__m256i_u*)&E[i * n[k] + j]);
                        __m256i vtR = _mm256_loadu_si256((__m256i_u*)&R[FirstNum * n[k] + j]);
                        vtE = _mm256_xor_si256(vtE, vtR);
                        _mm256_storeu_si256((__m256i_u*)&E[i * n[k] + j], vtE);
                    }
                } else {
                    int FirstNum = lp(&E[i * n[k]], n[k]);
                    for (int j = 0; j + 8 < n[k]; j += 8) {
                        _mm256_storeu_si256((__m256i_u*)&R[FirstNum * n[k] + j], _mm256_loadu_si256((__m256i_u*)&E[i * n[k] + j]));
                        _mm256_storeu_si256((__m256i_u*)&E[i * n[k] + j], _mm256_setzero_si256());
                    }
                    break;
                }

                IsNull = 0;
                IsNullHelp = _mm256_setzero_si256();

                for (int j = 0; j + 8 < n[k]; j += 8) {
                    __m256i IsNullHelpE = _mm256_loadu_si256((__m256i_u*)&E[i * n[k] + j]);
                    IsNullHelp = _mm256_or_si256(IsNullHelp, IsNullHelpE);
                }

                IsNullHelp = _mm256_or_si256(IsNullHelp, _mm256_permute2x128_si256(IsNullHelp, IsNullHelp, 0x81));
                IsNullHelp = _mm256_or_si256(IsNullHelp, _mm256_shuffle_epi32(IsNullHelp, _MM_SHUFFLE(0, 0, 0, 0)));
                temp = (int*)&IsNullHelp;
                IsNull |= temp[0] | temp[4];
            }
        }

        auto t2 = chrono::high_resolution_clock::now();
        chrono::duration<double, std::milli> fp_ms = t2 - t1;
        auto int_ms = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
        chrono::duration<long, std::micro> int_usec = int_ms;

        cout << k + 1 << "00: " << fp_ms.count() / 1000.0 << "s" << endl;

        delete[] E;
        delete[] R;
    }

    return 0;
}
